﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using ShadowEngine;
using Tao.OpenGl;
using ShadowEngine.OpenGL;
using ShadowEngine.ContentLoading;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace CarRace
{
    public partial class MainForm : Form
    {
        UDPer udp;

        string mycar = "Blue";
        public int mycar_id;
        public int mycar_index;
        public int oppcar_id;
        public int oppcar_index;

        uint hdc;
        int selectedCamara = 1;
        // Scenario 1 = crossing streetlight
        // Scenario 2 = crossing intersection with no streetlight
        int selectedScenario = 2;
        int count;
        int mostrado = 0;
        int movingUpDown;
        int movingLeftRight;
        bool KeyUpPressed = false;
        bool KeyDownPressed = false;
        bool KeyLeftPressed = false;
        bool KeyRightPressed = false;
        bool Collided = false;
        //CommunicateForm comForm = new CommunicateForm();
        Controller control;

        public MainForm()
        {
            InitializeComponent();

            control = new Controller(this);

            if(mycar == "Red")
            {
                mycar_id = 1;
                mycar_index = 1;
                oppcar_id = 2;
                oppcar_index = 0;
                WhoAmI.ForeColor = Color.Red;
                WhoAmI.Text = "I am Red";
            }
            else
            {
                mycar_id = 2;
                mycar_index = 0;
                oppcar_id = 1;
                oppcar_index = 1;
                WhoAmI.ForeColor = Color.Blue;
                WhoAmI.Text = "I am Blue";
            }

            //comForm.Show();
            // identificator of the position to draw
            hdc = (uint)this.Handle;
            // got the error
            string error = "";
            // Command of drawing initilization
            OpenGLControl.OpenGLInit(ref hdc, this.Width, this.Height, ref error);

            // set the position of the camera with perspective angle
            control.Camera.SetPerspective();
            if (error != "")
            {
                MessageBox.Show("Ocurrio un error al inicializar openGL");
                this.Close();   
            }

            // habilitate lights
            
            //float[] lightAmbient = { 0.15F, 0.15F, 0.15F, 0.0F };

            //Lighting.LightAmbient = lightAmbient; 
            
            Lighting.SetupLighting();  // encapsulate the shadow engine
            
            ContentManager.SetTextureList("texturas\\");
            ContentManager.LoadTextures();
            ContentManager.SetModelList("modelos\\");
            ContentManager.LoadModels();  
            control.CreateObjects(selectedScenario);

            udp = new UDPer(this, control.MessageCloud);
            udp.SetCar(control.cars[mycar_index]);
            udp.SetOppositeCar(control.cars[oppcar_index]);
            udp.Start();

            //Gl.glPolygonMode(Gl.GL_FRONT_AND_BACK, Gl.GL_LINE);   
        }

        public class UDPer
        {
            MainForm form;
            Cloud cloud;
            Car car = null;
            Car opposite_car = null;
            const int PORT_NUMBER = 15000;

            Thread t = null;
            public void Start()
            {
                if (t != null)
                {
                    throw new Exception("Already started, stop first");
                }
                //Console.WriteLine("Started listening");
                StartListening();
            }

            public UDPer(MainForm _form, Cloud Cloud_)
            {
                form = _form;
                cloud = Cloud_;
            }

            public void SetCar(Car _car)
            {
                car = _car;
            }

            public void SetOppositeCar(Car _car)
            {
                opposite_car = _car;
            }

            public void Stop()
            {
                try
                {
                    udp.Close();
                    //Console.WriteLine("Stopped listening");
                }
                catch { /* don't care */ }
            }

            private readonly UdpClient udp = new UdpClient(PORT_NUMBER);
            IAsyncResult ar_ = null;

            private void StartListening()
            {
                ar_ = udp.BeginReceive(Receive, new object());
            }
            private void Receive(IAsyncResult ar)
            {
                IPEndPoint ip = new IPEndPoint(IPAddress.Any, PORT_NUMBER);
                byte[] bytes = udp.EndReceive(ar, ref ip);
                //string message = Encoding.ASCII.GetString(bytes);
                //Console.WriteLine("From {0} received: {1} ", ip.Address.ToString(), message);
                // Read
                //MessageBox.Show(message);
                int type = BitConverter.ToInt32(new byte[] { bytes[0], bytes[1], bytes[2], bytes[3] } ,0);
                int id = BitConverter.ToInt32(new byte[] { bytes[4], bytes[5], bytes[6], bytes[7] }, 0);
                if (type == 1)
                {
                    //string[] part = message.Split(':');
                    if (id == form.oppcar_id)
                    {
                        form.StartFunction();
                    }
                }
                else if (type == 2)
                {
                    //string[] part = message.Split(':');
                    if (id == form.oppcar_id)
                    {
                        form.ResetFunction();
                    }
                }
                else if(type == 3)
                {
                    //string[] part = message.Split(':');
                    if(id == form.oppcar_id)
                    {
                        float x_speed = BitConverter.ToSingle(new byte[] { bytes[8], bytes[9], bytes[10], bytes[11] }, 0);
                        float y_speed = BitConverter.ToSingle(new byte[] { bytes[12], bytes[13], bytes[14], bytes[15] }, 0);
                        opposite_car.speed_x = x_speed;
                        opposite_car.speed_y = y_speed;
                    }
                }
                else
                {
                    float x_speed = BitConverter.ToSingle(new byte[] { bytes[8], bytes[9], bytes[10], bytes[11] }, 0);
                    float y_speed = BitConverter.ToSingle(new byte[] { bytes[12], bytes[13], bytes[14], bytes[15] }, 0);
                    int time = BitConverter.ToInt32(new byte[] { bytes[16], bytes[17], bytes[18], bytes[19] }, 0);
                    //string[] part = message.Split(':');
                    Terminal car_get = null;
                    if (Controller.StartedRace)
                    {
                        foreach(Terminal term in form.control.MessageCloud.terminals)
                        {
                            if (term.GetType() == typeof(Car))
                            {
                                Car cart = (Car)term;
                                if (cart.id == id)
                                {
                                    car_get = (Terminal)cart;
                                    cloud.Send(car_get, id, x_speed, y_speed, time);
                                }
                                    
                            }
                        }
                    }
                        //car.Read(id, x_speed, y_speed, time);
                }
                /*if (Int32.Parse(part[0]) == form.oppcar_id)
                {
                    opposite_car.traveledDistance_x = (float)Double.Parse(part[1]);
                    opposite_car.traveledDistance_y = (float)Double.Parse(part[2]);
                    opposite_car.current_pos.x = (float)Double.Parse(part[1]);
                    opposite_car.current_pos.y = (float)Double.Parse(part[2]);
                }*/
                StartListening();
            }
            public void Send(byte[] bytes)
            {
                //MessageBox.Show(message);
                UdpClient client = new UdpClient();
                IPEndPoint ip = new IPEndPoint(IPAddress.Parse("255.255.255.255"), PORT_NUMBER);
                //byte[] bytes = Encoding.ASCII.GetBytes(message);
                client.Send(bytes, bytes.Length, ip);
                client.Close();
                //Console.WriteLine("Sent: {0} ", message);
            }
        }

        public void UpdateLogic()
        {           
            if (movingUpDown == 1)
            {
                Gl.glTranslatef(0, 0, 0.35f); //0.35f
            }
            else if (movingUpDown == -1)
            {
                Gl.glTranslatef(0, 0, -0.35f); //-0.35f
            }
            else if (movingLeftRight == 1)
            {
                Gl.glTranslatef(0.35f, 0, 0);
            }
            else if (movingLeftRight == -1)
            {
                Gl.glTranslatef(-0.35f, 0, 0);
            }
            count++;
            if (Controller.FinishedRace == true && mostrado == 0)
            {
                mostrado = 1;
                movingUpDown = 0;
                //MessageBox.Show("The winner was the: " + lblPrimero.Text);
            }
        }


        private void tmrPaint_Tick(object sender, EventArgs e)
        {
            /* teste
            if (KeyUpPressed)
            {
                if(control.cars[1].speed_y < 1.0f)
                    control.cars[1].speed_y += 0.4f / 4 / 54;
            }
            else
            {
                if (control.cars[1].speed_y > 0.02f)
                    control.cars[1].speed_y -= 0.2f / 4 / 54;
                else
                    control.cars[1].speed_y = 0;
            }*/
            if(mycar == "Red")
            {
                label7.Text = Math.Ceiling(control.cars[mycar_index].speed_y * 216).ToString() + " km/h";
                label8.Text = Math.Ceiling(control.cars[mycar_index].speed_y * 134.216).ToString() + " mph";
            }
            else
            {
                label7.Text = Math.Ceiling(control.cars[mycar_index].speed_x * 216).ToString() + " km/h";
                label8.Text = Math.Ceiling(control.cars[mycar_index].speed_x * 134.216).ToString() + " mph";
            }

            UpdateLogic();
            //if (lblDistancia.Text != "0")
             //   comForm.SendData("Car 0 at (" + control.GetCarPosition(0).x + "," + control.GetCarPosition(0).y + "), Car 1 at(" + control.GetCarPosition(1).x + "," + control.GetCarPosition(1).y + "), Car 2 at(" + control.GetCarPosition(2).x + "," + control.GetCarPosition(2).y + ")");
            // clean opengl to draw
            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);
            //draws the entire scene
            control.DrawScene();

            // Test car
            /* ModelContainer m_teste;
            m_teste = ContentManager.GetModelByName("Touareg.3DS");
            m_teste.CreateDisplayList(); //optimice the model and load it in opengl display lists

            m_teste.ScaleX = 0.1f;
            m_teste.ScaleY = 0.1f;
            m_teste.ScaleZ = 0.1f;

            Position pos = new Position(2, 14);
            Gl.glPushMatrix();
            Gl.glTranslatef(pos.x, 0, pos.y);
            m_teste.DrawWithTextures();
            Gl.glPopMatrix();*/
            // ---------------

            // UDP stuff
            //blue car

            byte[] type_byte = BitConverter.GetBytes(0);
            byte[] id_byte = BitConverter.GetBytes(control.cars[mycar_index].id);
            byte[] x_byte = BitConverter.GetBytes(control.cars[mycar_index].GetPosition().x);
            byte[] y_byte = BitConverter.GetBytes(control.cars[mycar_index].GetPosition().y);
            byte[] time_byte = BitConverter.GetBytes(DateTime.Now.Second * 1000 + DateTime.Now.Millisecond);
            byte[] bytes = new byte[] {     type_byte[0], type_byte[1], type_byte[2], type_byte[3], 
                                            id_byte[0], id_byte[1], id_byte[2], id_byte[3],
                                            x_byte[0], x_byte[1], x_byte[2], x_byte[3],
                                            y_byte[0], y_byte[1], y_byte[2], y_byte[3],
                                            time_byte[0], time_byte[1], time_byte[2], time_byte[3]};
            udp.Send(bytes);

            //change buffers
            Winapi.SwapBuffers(hdc);
            //tell opengl to drop any operation he is doing and to prepare for a new frame
            Gl.glFlush();

            // Check collision
            if (control.CheckCollision() && !Collided)
            {
                Collided = true;
                mostrado = 1;
                movingUpDown = 0;
                //MessageBox.Show("Collided");
            }
            //comForm.AttachData(control.MessageCloud.RichEditData);

            WarningLabel.Text = control.cars[mycar_index].warning;

            if(mycar == "Red")
                label3.Text = Math.Ceiling(Math.Abs(control.cars[mycar_index].last_pred_speed_x * 216)).ToString() + " km/h";
            else
                label3.Text = Math.Ceiling(Math.Abs(control.cars[mycar_index].last_pred_speed_y * 216)).ToString() + " km/h";

            /*foreach (CarGotten element in control.cars[mycar_index].CarGotten_List)
            {
                float x_speed = 0, y_speed = 0;
                if(element.id == oppcar_id)
                {
                    float c = 0, b = 0, d = 0;
                    foreach(PositionGotten el in element.Position_List)
                    {
                        float time = (el.time - element.Position_List[0].time);
                        float x = (el.x - element.Position_List[0].x);
                        float y = (el.y - element.Position_List[0].y);
                        c += time * time;
                        b += time * x;
                        d += time * y;
                    }
                    x_speed = b / c * 25; //per tick
                    y_speed = d / c * 25; //per tick
                    label3.Text = Math.Ceiling(Math.Abs(x_speed * 216)).ToString() + " km/h";
                }
            }*/
        }

        private void MainForm_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            selectedCamara--;
            if (selectedCamara == 0)
            {
                selectedCamara = 4;
            }
            lblCamara.Text = Convert.ToString(selectedCamara);
            control.Camera.SelectCamera(selectedCamara - 1);    
        }

        private void button2_Click(object sender, EventArgs e)
        {
            selectedCamara++;
            if (selectedCamara == 5)
            {
                selectedCamara = 1;
            }
            lblCamara.Text = Convert.ToString(selectedCamara);
            control.Camera.SelectCamera(selectedCamara-1);   
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //udp.Send("START:" + mycar_id.ToString());
            //udp.Send(new byte[] { (byte)1, (byte)mycar_id });

            byte[] type_byte = BitConverter.GetBytes(1);
            byte[] id_byte = BitConverter.GetBytes(mycar_id);
            byte[] bytes = new byte[] {     type_byte[0], type_byte[1], type_byte[2], type_byte[3], 
                                            id_byte[0], id_byte[1], id_byte[2], id_byte[3]};
            udp.Send(bytes);

            StartFunction();
        }

        public void StartFunction()
        {
            Collided = false;
            control.StopTeste = false;
            Controller.StartedRace = true;   
        }

        private void btnReiniciar_Click(object sender, EventArgs e)
        {
            //udp.Send("RESET:"+mycar_id.ToString());
            //udp.Send(new byte[] { (byte)2, (byte)mycar_id });

            byte[] type_byte = BitConverter.GetBytes(2);
            byte[] id_byte = BitConverter.GetBytes(mycar_id);
            byte[] bytes = new byte[] {     type_byte[0], type_byte[1], type_byte[2], type_byte[3], 
                                            id_byte[0], id_byte[1], id_byte[2], id_byte[3]};
            udp.Send(bytes);

            //lblPrimero.Text = "None";
            //lblDistancia.Text = "0";
            //control.cars[0].CarGotten_List[0].Position_List = new List<PositionGotten>();
            //control.cars[1].CarGotten_List[0].Position_List = new List<PositionGotten>();
            ResetFunction();
        }

        public void ResetFunction()
        {
            label3.Text = "0 km/h";
            Collided = false;
            control.StopTeste = false;
            control.ResetRace();
            mostrado = 0;
            count = 0;
            control.cars[mycar_index].ClearCarGottenList();
            control.cars[oppcar_index].ClearCarGottenList();
        }

        private void MainForm_SizeChanged(object sender, EventArgs e)
        {
            Gl.glViewport(0, 0, this.Width, this.Height);
            // select the projection matrix
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            // the reset
            Gl.glLoadIdentity();
            //45 = vision angle
            //1  = high proportion of width
            //0.1f = minimum distance to draw
            //1000 = maximum distance
            Glu.gluPerspective(55, this.Width/(float)this.Height  , 0.1f, 1000);
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            control.Camera.SelectCamera(selectedCamara - 1); 
        }

        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.W)
            {
                movingUpDown = 1;
            }
            if (e.KeyData == Keys.S)
            {
                movingUpDown = -1;
            }
            if (e.KeyData == Keys.A)
            {
                movingLeftRight = 1;
            }
            if (e.KeyData == Keys.D)
            {
                movingLeftRight = -1;
            }
            if (e.KeyData == Keys.I)
            {
                KeyUpPressed = true;
                //if (Controller.StartedRace) movingUpDown = 1;
            }
            if (e.KeyData == Keys.Left)
            {
                KeyLeftPressed = true;
            }
            if (e.KeyData == Keys.Right)
            {
                KeyRightPressed = true;
            }
            if (e.KeyData == Keys.Down)
            {
                KeyDownPressed = true;
            }
        }

        private void MainForm_KeyUp(object sender, KeyEventArgs e)
        {
            movingUpDown = 0;
            movingLeftRight = 0;
            KeyUpPressed = false;
            KeyDownPressed = false;
            KeyLeftPressed = false;
            KeyRightPressed = false;
        }

        private void button4_Click(object sender, EventArgs e)
        {
            // Compose a string that consists of three lines.
            string lines = "";
            //string lines2 = "";

            foreach (CarGotten element in control.cars[mycar_index].CarGotten_List) // my car
            {
                if (element.id == oppcar_id)
                {
                    foreach (PositionGotten el in element.Position_List)
                    {
                        lines += (el.x - element.Position_List[0].x).ToString() + " " + (el.y - element.Position_List[0].y).ToString() + " " + (el.time - element.Position_List[0].time).ToString() + ";";
                        //lines2 += Math.Round(el.x - element.Position_List[0].x, 3).ToString() + " " + Math.Round(el.y - element.Position_List[0].y, 3).ToString() + " " + (el.time - element.Position_List[0].time).ToString() + ";";
                    }
                }
            }

            // Write the string to a file.
            System.IO.StreamWriter file = new System.IO.StreamWriter("test.txt");
            //System.IO.StreamWriter file2 = new System.IO.StreamWriter("test2.txt");
            file.WriteLine("["+lines+"]");
            //file2.WriteLine("[" + lines2 + "]");

            file.Close();
            //file2.Close();
        }

        private void numericUpDown1_ValueChanged(object sender, EventArgs e)
        {
            if(mycar == "Red")
            {
                control.cars[mycar_index].speed_y = (float)numericUpDown1.Value / 216;
                //udp.Send();
                //udp.Send("SPEED:"+mycar_id.ToString()+":"+control.cars[mycar_index].speed_x.ToString()+":"+control.cars[mycar_index].speed_y.ToString());
            }
            else
            {
                control.cars[mycar_index].speed_x = (float)numericUpDown1.Value / 216;
                //udp.Send("SPEED:"+ mycar_id.ToString() +":"+ control.cars[mycar_index].speed_x.ToString()+":"+control.cars[mycar_index].speed_y.ToString());

            }
            byte[] type_byte = BitConverter.GetBytes(3);
            byte[] id_byte = BitConverter.GetBytes(mycar_id);
            byte[] speed_x_byte = BitConverter.GetBytes(control.cars[mycar_index].speed_x);
            byte[] speed_y_byte = BitConverter.GetBytes(control.cars[mycar_index].speed_y);
            byte[] bytes = new byte[] {     type_byte[0], type_byte[1], type_byte[2], type_byte[3], 
                                            id_byte[0], id_byte[1], id_byte[2], id_byte[3],
                                            speed_x_byte[0], speed_x_byte[1], speed_x_byte[2], speed_x_byte[3],
                                            speed_y_byte[0], speed_y_byte[1], speed_y_byte[2], speed_y_byte[3]};
            udp.Send(bytes);
        }

    }
}
